package com.chaosj.xml;

import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.squareup.javapoet.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang.StringUtils;

import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;

/**
 * @className: GeneratePojo
 * @description:
 * @author: caoyangjie
 * @date: 2022/5/1
 **/
@Data
public class GeneratePojo {
    static final String ARRAY = "array";
    static final String REQUEST = "Request";
    static final String RESPONSE = "Response";
    static final String REQUEST1 = "request";
    static final String SYS_HEAD = "SysHead";
    static final String APP_HEAD = "AppHead";
    static final String APP_BODY = "Body";
    static final String SERVICE = "Service";
    static final String CALL = "call";
    static final String I = "I";
    private String serviceCode;
    private Request request = new Request();
    private Response response = new Response();

    @Override
    public String toString() {
        return "GeneratePojo{" +
                "serviceCode='" + serviceCode + '\'' +"\n"+
                ", request=" + request + "\n" +
                ", response=" + response + "\n"+
                '}';
    }

    @Data
    public static class Request implements IFunc<Request.SysHead, Request.AppHead, Request.Body> {
        private SysHead sysHead = new SysHead();
        private AppHead appHead = new AppHead();
        private Body body = new Body();

        @Data
        public static class SysHead implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Data
        public static class AppHead implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Data
        public static class Body implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Override
        public String toString() {
            return "Request{" +
                    "\nsysHead=" + sysHead +
                    "\n, appHead=" + appHead +
                    "\n, body=" + body +
                    '}'+"\n";
        }
    }

    @Data
    public static class Response implements IFunc<Response.SysHead, Response.AppHead, Response.Body>{
        private SysHead sysHead = new SysHead();
        private AppHead appHead = new AppHead();
        private Body body = new Body();

        @Data
        public static class SysHead implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Data
        public static class AppHead implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Data
        public static class Body implements IItem{
            private List<FieldItem> items = new ArrayList<>();
            private List<ClassItem> classItems = new ArrayList<>();
        }

        @Override
        public String toString() {
            return "Response{" +
                    "\nsysHead=" + sysHead +
                    "\n, appHead=" + appHead +
                    "\n, body=" + body +
                    '}'+"\n";
        }
    }

    public interface IFunc<S,A,B>{
        S getSysHead();
        A getAppHead();
        B getBody();
    }

    public interface IItem{
        List<FieldItem> getItems();
        List<ClassItem> getClassItems();
    }

    @Data
    public static class ClassItem{
        private String type;
        private String className;
        private List<FieldItem> items = new ArrayList<>();
        private List<ClassItem> classItems = new ArrayList<>();
    }

    @Data
    @AllArgsConstructor
    public static class FieldItem{
        private String fieldName;
        private String aliasName;
        private String zhName;
        private String type;
        private boolean isStruct;

        @Override
        public String toString() {
            return "FieldItem{" +
                    "fieldName='" + fieldName + '\'' +
                    ", aliasName='" + aliasName + '\'' +
                    ", zhName='" + zhName + '\'' +
                    '}'+"\n";
        }
    }

    public void generateJava(String packageName, String fileDir){
        List<JavaFile> allFiles = new ArrayList<>();
        // 生成 RPC 调用 接口
//        allFiles.addAll(doGenerateRpcInterface(packageName));
        // 生成 RPC 请求 参数
        allFiles.addAll(doGenerateRequest(packageName));
        // 生成 RPC 响应 参数
        allFiles.addAll(doGenerateResponse(packageName));
        Paths.get(fileDir).toFile().mkdirs();
        allFiles.forEach(f->{
            try {
                f.writeTo(Paths.get(fileDir));
            } catch (IOException e) {
            }
        });
    }

    private Collection<? extends JavaFile> doGenerateRpcInterface(String packageName) {
        JavaFile rpcServiceFile = JavaFile.builder( packageName ,
                TypeSpec.interfaceBuilder(I +serviceCode+ SERVICE).addModifiers(Modifier.PUBLIC)
                        .addMethod(MethodSpec.methodBuilder(CALL).addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                        .addParameter(ParameterSpec.builder(ClassName.get(packageName+"."+serviceCode,serviceCode+ REQUEST), REQUEST1).build())
                        .returns(ClassName.get(packageName+"."+serviceCode,serviceCode+ RESPONSE)).build())
                        .build()
        ).build();
        return Arrays.asList(rpcServiceFile);
    }

    private Collection<? extends JavaFile> doGenerateRequest(String packageName) {
        TypeSpec.Builder requestBuilder = TypeSpec.classBuilder(serviceCode+REQUEST)
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Data.class);
        String packName = packageName+"."+serviceCode+"."+serviceCode+REQUEST;
        append(SYS_HEAD,request.getSysHead(), requestBuilder, packName);
        append(APP_HEAD,request.getAppHead(), requestBuilder, packName);
        append(APP_BODY,request.getBody(), requestBuilder, packName);

        JavaFile requestFile = JavaFile.builder(packageName+"."+serviceCode, requestBuilder.build()).build();
        return Arrays.asList(requestFile);
    }

    private TypeSpec.Builder append(String className, IItem item, TypeSpec.Builder builder, String packName){
        TypeSpec.Builder newClazzBuilder = TypeSpec.classBuilder(className)
                .addModifiers(Modifier.PUBLIC,Modifier.STATIC)
                .addAnnotation(Data.class);
        for( FieldItem field : item.getItems() ){
            append(field, newClazzBuilder);
        }
        for ( ClassItem clazz : item.getClassItems() ){
            append(clazz, newClazzBuilder, packName+"."+StrUtil.upperFirst(className));
        }
        builder.addType(newClazzBuilder.build())
                .addField(FieldSpec.builder(ClassName.get(packName,StrUtil.upperFirst(className)),StrUtil.lowerFirst(className),Modifier.PRIVATE)
                        .addAnnotation(AnnotationSpec.builder(JsonProperty.class).addMember("value", CodeBlock.builder().add(StrFormatter.format("value=\"{}\"",StrUtil.lowerFirst(className))).build()).build())
                        .build());
        return builder;
    }

    private TypeSpec.Builder append(FieldItem field, TypeSpec.Builder builder){
        if( ARRAY.equalsIgnoreCase(field.getType()) ){
            builder.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),ClassName.get(String.class)),StrUtil.lowerFirst(field.aliasName),Modifier.PRIVATE)
                    .addAnnotation(AnnotationSpec.builder(JsonProperty.class).addMember("value", CodeBlock.builder().add(StrFormatter.format("value=\"{}\"",field.getFieldName())).build()).build())
                    .build()
            );
        }else{
            builder.addField(FieldSpec.builder(ClassName.get(String.class),StrUtil.lowerFirst(field.aliasName),Modifier.PRIVATE)
                    .addAnnotation(AnnotationSpec.builder(JsonProperty.class).addMember("value", CodeBlock.builder().add(StrFormatter.format("value=\"{}\"",field.getFieldName())).build()).build())
                    .build());
        }
        return builder;
    }

    private TypeSpec.Builder append(ClassItem clazz, TypeSpec.Builder builder, String packName){
        TypeSpec.Builder newClazzBuilder = TypeSpec.classBuilder(StrUtil.upperFirst(clazz.getClassName())).addModifiers(Modifier.PUBLIC,Modifier.STATIC).addAnnotation(Data.class);
        for( FieldItem item : clazz.getItems() ){
            append(item, newClazzBuilder);
        }
        for( ClassItem item: clazz.getClassItems() ){
            append(item, newClazzBuilder, packName+"."+StrUtil.upperFirst(clazz.getClassName()));
        }
        if( ARRAY.equalsIgnoreCase(clazz.getType() ) || StringUtils.isBlank(clazz.getType()) ){
            builder.addType(newClazzBuilder.build())
                    .addField(
                            FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),ClassName.get(packName,StrUtil.upperFirst(clazz.className))),StrUtil.lowerFirst(clazz.className),Modifier.PRIVATE)
                            .addAnnotation(AnnotationSpec.builder(JsonProperty.class).addMember("value", CodeBlock.builder().add(StrFormatter.format("value=\"{}\"",clazz.getClassName())).build()).build())
                            .build()
                    );
        }else{
            builder.addType(newClazzBuilder.build())
                    .addField(
                            FieldSpec.builder(ClassName.get(packName,StrUtil.upperFirst(clazz.className)),StrUtil.lowerFirst(clazz.className),Modifier.PRIVATE)
                            .addAnnotation(AnnotationSpec.builder(JsonProperty.class).addMember("value", CodeBlock.builder().add(StrFormatter.format("value=\"{}\"",clazz.getClassName())).build()).build())
                            .build()
                    );
        }
        return builder;
    }

    private Collection<? extends JavaFile> doGenerateResponse(String packageName) {
        TypeSpec.Builder responseBuilder = TypeSpec.classBuilder(serviceCode+RESPONSE)
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Data.class);
        String packName = packageName+"."+serviceCode+"."+serviceCode+RESPONSE;
        append(SYS_HEAD,response.getSysHead(), responseBuilder, packName);
        append(APP_HEAD,response.getAppHead(), responseBuilder, packName);
        append(APP_BODY,response.getBody(), responseBuilder, packName);

        JavaFile requestFile = JavaFile.builder(packageName+"."+serviceCode, responseBuilder.build()).build();
        return Arrays.asList(requestFile);
    }
}
